﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using StockFinder.Model;
using StockFinder.Model.Enumerations;
using StockFinder.DataAccess;
using StockFinder.Services.Extractors.Yahoo;

namespace StockFinder.Services.Engines.Yahoo
{
    public class YahooDownloadHistoricPricesAllSymbolsEngine : IDownloadHistoricPricesAllSymbolsEngine
    {
        private readonly SymbolDataAccess _SymbolDataAccess;
        private readonly DailyPriceDataAccess _DailyPriceDataAccess;
        private readonly YahooHistoricPriceExtractor _HistoricPricesExtractor;

        private bool _IsRunning;

        public YahooDownloadHistoricPricesAllSymbolsEngine()
        {
            _SymbolDataAccess = new SymbolDataAccess();
            _DailyPriceDataAccess = new DailyPriceDataAccess();
            _HistoricPricesExtractor = new YahooHistoricPriceExtractor();
        }

        #region Event raisers

        protected virtual void OnLogMessage(string message)
        {
            if (LogMessage != null)
            {
                LogMessage(message);
            }
        }

        protected virtual void OnUpdateDiagnostics(YahooDownloadHistoricPricesDiagnostics diagnostics)
        {
            if (UpdateDiagnostics != null)
            {
                UpdateDiagnostics(diagnostics);
            }
        }

        #endregion

        #region IDownloadHistoricPricesAllSymbolsEngine Members

        public DateTime DateFrom { get; set; }
        public DateTime DateTo { get; set; }

        public event Action<YahooDownloadHistoricPricesDiagnostics> UpdateDiagnostics;

        public event Action<string> LogMessage;

        public void Start()
        {
            _IsRunning = true;

            #region Local variables

            OnLogMessage("Starting download of prices");

            OnLogMessage("Getting symbols");

            //first get all the symbols
            List<Symbol> symbols = SymbolDataAccess.GetAllSymbols();            

            HistoricPricesExtract historicPricesExtract = null;

            DailyPrice existingDailyPrice = null;

            Symbol symbol = null;

            DailyPrice dailyPrice = null;

            int newPricesInsertedCounter = 0;
            int symbolCounter = 0;
            int symbolsRemaining = 0;

            decimal averageSecondsTaken = 0;

            TimeSpan timeTaken = TimeSpan.Zero;
            TimeSpan timeRemaining = TimeSpan.Zero;

            DateTime timeStart = DateTime.Now;

            YahooDownloadHistoricPricesDiagnostics diagnostics = new YahooDownloadHistoricPricesDiagnostics();

            #endregion            

            #region Main loop

            OnLogMessage("Looping symbols");

            //then loop symbols
            for (int i = 0; i < symbols.Count; i++)
            {
                //check if service should be running
                if (_IsRunning)
                {
                    //increment symbol counter
                    symbolCounter++;

                    //reference indexed symbol
                    symbol = symbols[i];

                    OnLogMessage(string.Format("Getting existing prices for Symbol: {0}", symbol.Name));

                    //get the prices for this symbol
                    _DailyPriceDataAccess.GetAllDailyPricesBySymbolIdFromDate(symbol, DateFrom);

                    OnLogMessage(string.Format("Downloading prices for Symbol: {0}", symbol.Name));

                    //then get new prices for symbol
                    historicPricesExtract = _HistoricPricesExtractor.ExtractHistoricPrices(DateFrom, DateTo, symbol.Name);

                    //then check if any new prices already exist and insert if not
                    if (historicPricesExtract.HistoricPricesExtractResult == HistoricPricesExtractResult.SUCCESS)
                    {
                        OnLogMessage("Download new prices, comparing against existing");

                        //check there are new downloaded prices
                        if (historicPricesExtract.DailyPrices != null && historicPricesExtract.DailyPrices.Count > 0)
                        {
                            //reset counter
                            newPricesInsertedCounter = 0;

                            //loop new daily prices
                            for (int j = 0; j < historicPricesExtract.DailyPrices.Count; j++)
                            {
                                //reference extracted daily price
                                dailyPrice = historicPricesExtract.DailyPrices[j];

                                //does this daily price exist in existing
                                if (symbol.Prices.Count > 0)
                                {
                                    //find existing price by date
                                    existingDailyPrice = symbol.Prices.Find(p => p.PriceDate.Date == dailyPrice.PriceDate.Date);

                                    //not exist?
                                    if (existingDailyPrice == null)
                                    {
                                        //no - insert new price
                                        _DailyPriceDataAccess.InsertDailyPrice(dailyPrice);

                                        newPricesInsertedCounter++;
                                    }
                                }
                                else
                                {
                                    //no - insert new price
                                    _DailyPriceDataAccess.InsertDailyPrice(dailyPrice);

                                    newPricesInsertedCounter++;
                                }
                            }

                            OnLogMessage(string.Format("{0} new prices inserted", newPricesInsertedCounter));
                        }
                        else
                        {
                            OnLogMessage(string.Format("No prices downloaded", historicPricesExtract.Message));
                        }
                    }
                    else
                    {
                        OnLogMessage(string.Format("Error downloading prices: {0}", historicPricesExtract.Message));
                    }

                    #region Diagnostics

                    //record how long it took
                    timeTaken = DateTime.Now - timeStart;

                    //calculate the average time taken
                    averageSecondsTaken = (decimal)timeTaken.TotalSeconds / (decimal)symbolCounter;

                    symbolsRemaining = (symbols.Count - symbolCounter);

                    //estimated time left: average time taken X number of items left
                    timeRemaining = TimeSpan.FromSeconds((double)averageSecondsTaken * (double)symbolsRemaining);

                    //set diagnostics
                    diagnostics.AverageSecondsTaken = averageSecondsTaken;
                    diagnostics.ProgressMaximum = symbols.Count;
                    diagnostics.ProgressMinimum = 0;
                    diagnostics.ProgressValue = symbolCounter;
                    diagnostics.TimeRemaining = timeRemaining;

                    OnUpdateDiagnostics(diagnostics);

                    #endregion
                }
                else
                {
                    //thread will just quit
                    OnLogMessage("Thread stopped");
                }
            }

            #endregion

            OnLogMessage("Complete");
        }

        public void Stop()
        {
            //stop the service
            _IsRunning = false;
        }

        #endregion
    }
}
